package com.jessefyz.module.controller.shop;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jessefyz.aspectj.CurrentMember;
import com.jessefyz.common.core.domain.AjaxResult;
import com.jessefyz.common.exception.CustomException;
import com.jessefyz.common.exception.DataNotFoundException;
import com.jessefyz.common.exception.ParamException;
import com.jessefyz.module.member.domain.MemberInfo;
import com.jessefyz.module.service.CouponVerifyService;
import com.jessefyz.module.shop.constants.SystemConfig;
import com.jessefyz.module.shop.domain.*;
import com.jessefyz.module.shop.req.CartCheckUpdate;
import com.jessefyz.module.shop.req.CartDelete;
import com.jessefyz.module.shop.service.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.*;


/**
 * 用户购物车服务
 */
@RestController
@RequestMapping("/wx/cart")
@Validated
public class WxCartController {
    private final Log logger = LogFactory.getLog(WxCartController.class);

    @Autowired
    private ILitemallCartService cartService;
    @Autowired
    private ILitemallGoodsService goodsService;
    @Autowired
    private ILitemallGoodsProductService productService;
    @Autowired
    private ILitemallAddressService addressService;
    @Autowired
    private ILitemallGrouponRulesService grouponRulesService;
    @Autowired
    private ILitemallCouponService couponService;
    @Autowired
    private ILitemallCouponUserService couponUserService;
    @Autowired
    private CouponVerifyService couponVerifyService;

    /**
     * 用户购物车信息
     * @return 用户购物车信息
     */
    @GetMapping("index")
    public AjaxResult<Map<String, Object>> index(@ApiIgnore @CurrentMember MemberInfo currentMember) {
        List<LitemallCart> list = cartService.list(new LambdaQueryWrapper<LitemallCart>()
            .eq(LitemallCart::getUserId,currentMember.getId())
        );
        List<LitemallCart> cartList = new ArrayList<>();
        // TODO
        // 如果系统检查商品已删除或已下架，则系统自动删除。
        // 更好的效果应该是告知用户商品失效，允许用户点击按钮来清除失效商品。
        for (LitemallCart cart : list) {
            LitemallGoods goods = goodsService.getById(cart.getGoodsId());
            if (goods == null || 0 == goods.getIsOnSale().intValue()) {
                cartService.removeById(cart.getId());
            }
            else{
                cartList.add(cart);
            }
        }

        Integer goodsCount = 0;
        BigDecimal goodsAmount = new BigDecimal(0.00);
        Integer checkedGoodsCount = 0;
        BigDecimal checkedGoodsAmount = new BigDecimal(0.00);
        for (LitemallCart cart : cartList) {
            goodsCount += cart.getNumber();
            goodsAmount = goodsAmount.add(cart.getPrice().multiply(new BigDecimal(cart.getNumber())));
            if (1 == cart.getChecked().intValue()) {
                checkedGoodsCount += cart.getNumber();
                checkedGoodsAmount = checkedGoodsAmount.add(cart.getPrice().multiply(new BigDecimal(cart.getNumber())));
            }
        }
        Map<String, Object> cartTotal = new HashMap<>();
        cartTotal.put("goodsCount", goodsCount);
        cartTotal.put("goodsAmount", goodsAmount);
        cartTotal.put("checkedGoodsCount", checkedGoodsCount);
        cartTotal.put("checkedGoodsAmount", checkedGoodsAmount);

        Map<String, Object> result = new HashMap<>();
        result.put("cartList", cartList);
        result.put("cartTotal", cartTotal);

        return AjaxResult.success(result);
    }

    /**
     * 加入商品到购物车
     * <p>
     * 如果已经存在购物车货品，则增加数量；
     * 否则添加新的购物车货品项。
     *
     * @param
     * @param cart   购物车商品信息， { goodsId: xxx, productId: xxx, number: xxx }
     * @return 加入购物车操作结果
     */
    @PostMapping("add")
    public AjaxResult<Integer> add(@ApiIgnore @CurrentMember MemberInfo currentMember,@Valid @RequestBody LitemallCart cart) {

        Long productId = cart.getProductId();
        Long goodsId = cart.getGoodsId();
        //判断商品是否可以购买
        LitemallGoods goods = goodsService.getById(goodsId);
        if (goods == null || 0 == goods.getIsOnSale()) {
            throw new CustomException( "商品已下架");
        }

        LitemallGoodsProduct product = productService.getById(productId);
        //判断购物车中是否存在此规格商品
        LitemallCart existCart = cartService.getOne(new LambdaQueryWrapper<LitemallCart>()
            .eq(LitemallCart::getGoodsId,goodsId)
            .eq(LitemallCart::getProductId,productId)
            .eq(LitemallCart::getUserId,currentMember.getId())
        ,false);
        if (existCart == null) {
            //取得规格的信息,判断规格库存
            if (product == null || cart.getNumber() > product.getNumber()) {
                throw new CustomException("库存不足");
            }
            cart.setId(null);
            cart.setGoodsSn(goods.getGoodsSn());
            cart.setGoodsName((goods.getName()));
            if(StringUtils.isEmpty(product.getUrl())){
                cart.setPicUrl(goods.getPicUrl());
            }
            else{
                cart.setPicUrl(product.getUrl());
            }
            cart.setPrice(product.getPrice());
            cart.setSpecifications(product.getSpecifications());
            cart.setUserId(currentMember.getId());
            cart.setChecked(1);
            cartService.save(cart);
        } else {
            //取得规格的信息,判断规格库存
            int num = existCart.getNumber() + cart.getNumber();
            if (num > product.getNumber()) {
                throw new CustomException("库存不足");
            }
            existCart.setNumber( num);
            if (!cartService.updateById(existCart)) {
                throw new CustomException("修改失败");
            }
        }
        //更新
        return goodscount(currentMember);
    }

    /**
     * 立即购买
     * <p>
     * 和add方法的区别在于：
     * 1. 如果购物车内已经存在购物车货品，前者的逻辑是数量添加，这里的逻辑是数量覆盖
     * 2. 添加成功以后，前者的逻辑是返回当前购物车商品数量，这里的逻辑是返回对应购物车项的ID
     *
     * @param cart   购物车商品信息， { goodsId: xxx, productId: xxx, number: xxx }
     * @return 立即购买操作结果
     */
    @PostMapping("fastadd")
    public AjaxResult<Long> fastadd(@ApiIgnore @CurrentMember MemberInfo currentMember,@Valid @RequestBody LitemallCart cart) {
        //判断商品是否可以购买
        LitemallGoods goods = goodsService.getById(cart.getGoodsId());
        if (goods == null || 1 != goods.getIsOnSale().intValue()) {
            throw new CustomException("商品已下架");
        }

        LitemallGoodsProduct product = productService.getById(cart.getProductId());
        if (null == product) {
            throw new DataNotFoundException("商品产品不存在");
        }
        //判断购物车中是否存在此规格商品
        LitemallCart existCart = cartService.getOne(new LambdaQueryWrapper<LitemallCart>()
            .eq(LitemallCart::getGoodsId,cart.getGoodsId())
            .eq(LitemallCart::getProductId,cart.getProductId())
            .eq(LitemallCart::getUserId,currentMember.getId())
        ,false);
        if (existCart == null) {
            //取得规格的信息,判断规格库存
            if (product == null || cart.getNumber() > product.getNumber()) {
                throw new CustomException("库存不足");
            }
            cart.setId(null);
            cart.setGoodsSn(goods.getGoodsSn());
            cart.setGoodsName((goods.getName()));
            if(StringUtils.isEmpty(product.getUrl())){
                cart.setPicUrl(goods.getPicUrl());
            }
            else{
                cart.setPicUrl(product.getUrl());
            }
            cart.setPrice(product.getPrice());
            cart.setSpecifications(product.getSpecifications());
            cart.setUserId(currentMember.getId());
            cart.setChecked(1);
            cart.setAddTime(new Date());
            cart.setUpdateTime(new Date());
            cartService.save(cart);
            existCart = cart;
        } else {
            //取得规格的信息,判断规格库存
            int num = cart.getNumber();
            if (num > product.getNumber()) {
                throw new CustomException("库存不足");
            }
            existCart.setNumber(num);
            if (!cartService.updateById(existCart)) {
                throw new CustomException("更新库存失败,请联系管理员！");
            }
        }
        return AjaxResult.success(existCart.getId());
    }

    /**
     * 修改购物车商品货品数量
     *
     * @param cart   购物车商品信息， { id: xxx, goodsId: xxx, productId: xxx, number: xxx }
     * @return 修改结果
     */
    @PostMapping("update")
    public Object update(@ApiIgnore @CurrentMember MemberInfo currentMember,@Valid @RequestBody LitemallCart cart) {
        //判断是否存在该订单
        // 如果不存在，直接返回错误
        LitemallCart existCart = cartService.getOne(new LambdaQueryWrapper<LitemallCart>()
            .eq(LitemallCart::getId,cart.getId())
            .eq(LitemallCart::getUserId,currentMember.getId())
        ,false);
        if (existCart == null) {
            throw new DataNotFoundException("购物车数据不存在");
        }
        // 判断goodsId和productId是否与当前cart里的值一致
        if (!existCart.getGoodsId().equals(cart.getGoodsId())) {
            throw new CustomException("参数异常");
        }
        if (!existCart.getProductId().equals(cart.getProductId())) {
            throw new CustomException("参数异常");
        }

        //判断商品是否可以购买
        LitemallGoods goods = goodsService.getById(cart.getGoodsId());
        if (goods == null || 0 == goods.getIsOnSale()) {
            throw new CustomException("商品已下架");
        }

        //取得规格的信息,判断规格库存
        LitemallGoodsProduct product = productService.getById(cart.getProductId());
        if (product == null || product.getNumber() < cart.getNumber()) {
            throw new CustomException( "库存不足");
        }

        existCart.setNumber(cart.getNumber());
        cartService.updateById(existCart);
        return AjaxResult.success();
    }

    /**
     * 购物车商品货品勾选状态
     * <p>
     * 如果原来没有勾选，则设置勾选状态；如果商品已经勾选，则设置非勾选状态。
     *
     * @param cartCheckUpdate 购物车商品信息， { productIds: xxx, isChecked: 1/0 }
     * @return 购物车信息
     */
    @PostMapping("checked")
    public AjaxResult<Map<String, Object>> checked(@ApiIgnore @CurrentMember MemberInfo currentMember, @Valid @RequestBody CartCheckUpdate cartCheckUpdate) {
        cartService.update(new LambdaUpdateWrapper<LitemallCart>()
            .set(LitemallCart::getChecked,cartCheckUpdate.getIsChecked())
            .eq(LitemallCart::getUserId,currentMember.getId())
            .in(LitemallCart::getProductId,cartCheckUpdate.getProductIds())
        );
        return index(currentMember);
    }

    /**
     * 购物车商品删除
     *
     * @param currentMember 当前登录用户
     * @param cartDelete 购物车商品信息， { productIds: xxx }
     * @return 购物车信息
     * 成功则
     * {
     * errno: 0,
     * errmsg: '成功',
     * data: xxx
     * }
     * 失败则 { errno: XXX, errmsg: XXX }
     */
    @PostMapping("delete")
    public AjaxResult<Map<String, Object>> delete(@ApiIgnore @CurrentMember MemberInfo currentMember,
                                                  @Valid @RequestBody CartDelete cartDelete) {
        cartService.remove(new LambdaQueryWrapper<LitemallCart>()
            .eq(LitemallCart::getUserId,currentMember.getId())
            .in(LitemallCart::getProductId,cartDelete.getProductIds())
        );
        return index(currentMember);
    }

    /**
     * 购物车商品货品数量
     * <p>
     * 如果用户没有登录，则返回空数据。
     *
     * @return 购物车商品货品数量
     */
    @GetMapping("goodscount")
    public AjaxResult<Integer> goodscount(@ApiIgnore @CurrentMember MemberInfo currentMember) {
        int goodsCount = 0;
        List<LitemallCart> cartList = cartService.list(new LambdaQueryWrapper<LitemallCart>()
            .eq(LitemallCart::getUserId,currentMember.getId())
        );
        for (LitemallCart cart : cartList) {
            goodsCount += cart.getNumber();
        }

        return AjaxResult.success(goodsCount);
    }

    /**
     * 购物车下单
     *
     * @param currentMember 当前用户
     * @param cartId    购物车商品ID：
     *                  如果购物车商品ID是空，则下单当前用户所有购物车商品；
     *                  如果购物车商品ID非空，则只下单当前购物车商品。
     * @param addressId 收货地址ID：
     *                  如果收货地址ID是空，则查询当前用户的默认地址。
     * @param couponId  优惠券ID：
     *                  如果优惠券ID是空，则自动选择合适的优惠券。
     * @return 购物车操作结果
     */
    @GetMapping("checkout")
    public Object checkout(@ApiIgnore @CurrentMember MemberInfo currentMember,
                           Integer cartId, Long addressId, Long couponId, Long userCouponId, Long grouponRulesId) {
        // 收货地址
        LitemallAddress checkedAddress = null;
        if (null == addressId || addressId.equals(0L)) {
            checkedAddress = addressService.getOne(new LambdaQueryWrapper<LitemallAddress>()
                .eq(LitemallAddress::getUserId,currentMember.getId())
                .eq(LitemallAddress::getIsDefault,1)
            ,false);
            // 如果仍然没有地址，则是没有收货地址
            // 返回一个空的地址id=0，这样前端则会提醒添加地址
            if (checkedAddress == null) {
                checkedAddress = new LitemallAddress();
                checkedAddress.setId(0L);
                addressId = 0L;
            } else {
                addressId = checkedAddress.getId();
            }

        } else {
            checkedAddress = addressService.getOne(new LambdaQueryWrapper<LitemallAddress>()
                .eq(LitemallAddress::getId,addressId)
                .eq(LitemallAddress::getUserId,currentMember.getId())
            );
            // 如果null, 则报错
            if (checkedAddress == null) {
                throw new ParamException("参数异常");
            }
        }

        // 团购优惠
        BigDecimal grouponPrice = new BigDecimal(0.00);
        LitemallGrouponRules grouponRules = grouponRulesService.getById(grouponRulesId);
        if (null != grouponRules) {
            grouponPrice = grouponRules.getDiscount();
        }

        // 商品价格
        List<LitemallCart> checkedGoodsList = null;
        if (cartId == null || cartId.equals(0)) {
            checkedGoodsList = cartService.list(new LambdaQueryWrapper<LitemallCart>()
                .eq(LitemallCart::getUserId,currentMember.getId())
                .eq(LitemallCart::getChecked,1)
            );
        } else {
            LitemallCart cart = cartService.getOne(new LambdaQueryWrapper<LitemallCart>()
                    .eq(LitemallCart::getId,cartId)
                    .eq(LitemallCart::getUserId,currentMember.getId())
            ,false);
            if (cart == null) {
                throw new DataNotFoundException("购物车数据不存在");
            }
            checkedGoodsList = new ArrayList<>(1);
            checkedGoodsList.add(cart);
        }
        BigDecimal checkedGoodsPrice = new BigDecimal(0.00);
        for (LitemallCart cart : checkedGoodsList) {
            //  只有当团购规格商品ID符合才进行团购优惠
            if (grouponRules != null && grouponRules.getGoodsId().equals(cart.getGoodsId())) {
                checkedGoodsPrice = checkedGoodsPrice.add(cart.getPrice().subtract(grouponPrice).multiply(new BigDecimal(cart.getNumber())));
            } else {
                checkedGoodsPrice = checkedGoodsPrice.add(cart.getPrice().multiply(new BigDecimal(cart.getNumber())));
            }
        }
        // 计算优惠券可用情况
        BigDecimal tmpCouponPrice = new BigDecimal(0.00);
        Long tmpCouponId = 0L;
        Long tmpUserCouponId = 0L;
        int tmpCouponLength = 0;
        List<LitemallCouponUser> couponUserList = couponUserService.list(new LambdaQueryWrapper<LitemallCouponUser>()
            .eq(LitemallCouponUser::getUserId,currentMember.getId())
            .eq(LitemallCouponUser::getStatus,0)
            .orderByDesc(LitemallCouponUser::getAddTime)
        );
        for(LitemallCouponUser couponUser : couponUserList){
            LitemallCoupon coupon = couponVerifyService.checkCoupon(currentMember.getId(), couponUser.getCouponId(), couponUser.getId(), checkedGoodsPrice, checkedGoodsList);
            if(coupon == null){
                continue;
            }

            tmpCouponLength++;
            if(tmpCouponPrice.compareTo(coupon.getDiscount()) == -1){
                tmpCouponPrice = coupon.getDiscount();
                tmpCouponId = coupon.getId();
                tmpUserCouponId = couponUser.getId();
            }
        }
        // 获取优惠券减免金额，优惠券可用数量
        int availableCouponLength = tmpCouponLength;
        BigDecimal couponPrice = new BigDecimal(0);
        // 这里存在三种情况
        // 1. 用户不想使用优惠券，则不处理
        // 2. 用户想自动使用优惠券，则选择合适优惠券
        // 3. 用户已选择优惠券，则测试优惠券是否合适
        if (couponId == null || couponId.equals(-1)){
            couponId = -1L;
            userCouponId = -1L;
        }
        else if (couponId.equals(0)) {
            couponPrice = tmpCouponPrice;
            couponId = tmpCouponId;
            userCouponId = tmpUserCouponId;
        }
        else {
            LitemallCoupon coupon = couponVerifyService.checkCoupon(currentMember.getId(), couponId, userCouponId, checkedGoodsPrice, checkedGoodsList);
            // 用户选择的优惠券有问题，则选择合适优惠券，否则使用用户选择的优惠券
            if(coupon == null){
                couponPrice = tmpCouponPrice;
                couponId = tmpCouponId;
                userCouponId = tmpUserCouponId;
            }
            else {
                couponPrice = coupon.getDiscount();
            }
        }

        // 根据订单商品总价计算运费，满88则免运费，否则8元；
        BigDecimal freightPrice = new BigDecimal(0.00);
        if (checkedGoodsPrice.compareTo(SystemConfig.getFreightLimit()) < 0) {
            freightPrice = SystemConfig.getFreight();
        }

        // 可以使用的其他钱，例如用户积分
        BigDecimal integralPrice = new BigDecimal(0.00);

        // 订单费用
        BigDecimal orderTotalPrice = checkedGoodsPrice.add(freightPrice).subtract(couponPrice).max(new BigDecimal(0.00));

        BigDecimal actualPrice = orderTotalPrice.subtract(integralPrice);

        Map<String, Object> data = new HashMap<>();
        data.put("addressId", addressId);
        data.put("couponId", couponId);
        data.put("userCouponId", userCouponId);
        data.put("cartId", cartId);
        data.put("grouponRulesId", grouponRulesId);
        data.put("grouponPrice", grouponPrice);
        data.put("checkedAddress", checkedAddress);
        data.put("availableCouponLength", availableCouponLength);
        data.put("goodsTotalPrice", checkedGoodsPrice);
        data.put("freightPrice", freightPrice);
        data.put("couponPrice", couponPrice);
        data.put("orderTotalPrice", orderTotalPrice);
        data.put("actualPrice", actualPrice);
        data.put("checkedGoodsList", checkedGoodsList);
        return AjaxResult.success(data);
    }
}
